Skip to content

Conversation

@papertray3
Copy link

Summary

This PR adds support for retrieving rendered HTML content from markdown files using HTTP content negotiation. Clients can now request fully-rendered HTML by specifying Accept: application/vnd.olrapi.note+html in GET requests.

Changes

  • New Content Type: Added ContentTypes.olrapiNoteHtml constant (application/vnd.olrapi.note+html)
  • HTML Rendering: Implemented renderMarkdownToHtml() method using Obsidian's native MarkdownRenderer.render() API
  • Request Handling: Updated _vaultGet() to handle HTML content type requests
  • Documentation: Updated OpenAPI specification with new content format examples
  • Memory Management: Proper component lifecycle management to prevent memory leaks

Features

The rendered HTML output includes:

  • Wiki-links converted to HTML anchors with proper data-href attributes
  • Obsidian-specific markdown (callouts, embeds, transclusions)
  • Plugin integrations preserved (e.g., Metadata Menu icons)
  • Syntax-highlighted code blocks
  • Full semantic HTML structure

API Usage

# Get rendered HTML
curl -k https://127.0.0.1:27124/vault/path/to/note.md \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/vnd.olrapi.note+html"

Backward Compatibility

This feature:

  • Uses standard HTTP content negotiation (Accept header)
  • Does not modify existing behavior for markdown or JSON responses
  • Works with all GET endpoints (/vault/, /active/, /periodic/)
  • Is fully optional - defaults to existing markdown behavior

Testing

Tested with:

  • Various markdown files with wiki-links, embeds, and frontmatter
  • Complex documents with Dataview queries
  • Plugin integrations (Metadata Menu)
  • Both /vault/{filename} and /active/ endpoints

Use Cases

This feature enables:

  • Digital garden publishing workflows with preview capabilities
  • External rendering of Obsidian notes
  • Wiki-link validation before publishing
  • Integration with static site generators

🤖 Generated with Claude Code

@papertray3
Copy link
Author

Follow-up: I discovered Dataview/DataviewJS blocks were still empty unless the renderer had a real DOM host. The latest commit
attaches a hidden container to document.body, renders there, waits for dynamic blocks to settle, then removes it. This ensures the HTML
returned by /vault matches what Obsidian shows (full Workbench/Queue/Vault stats, etc.). Added coverage for the Accept: application/
vnd.olrapi.note+html path while I was here.

Implement DOM-based extraction of rendered Obsidian content with
support for both plain text and structured JSON output formats.

Features:
- rendered-text content type: Plain text extraction from rendered DOM
- rendered-json content type: Structured JSON with typed content blocks
- Cache system for fast repeated access
- Full Dataview/DataviewJS rendering support
- Settings UI for cache configuration
- Automatic cache invalidation on file changes

Content Types:
- application/vnd.olrapi.note+rendered-text (plain text)
- application/vnd.olrapi.note+rendered-json (structured JSON)

JSON Structure:
- metadata: source path, render timestamp, format version
- frontmatter: complete YAML frontmatter as object
- content: array of typed blocks (heading, table, list, paragraph, code, callout)

Table blocks include headers and rows as 2D arrays, making them
easy to parse and analyze programmatically.

Implementation:
- RenderCacheManager: handles rendering and caching
- StructuredExtractor: converts DOM to typed JSON blocks
- Cache stored in .obsidian/render-cache/ with hash-based keys
- 2-second content settlement wait for async plugin rendering
- Restores original user view after extraction

Benefits for AI Clients:
- Tables as parseable 2D arrays
- Document structure preserved with type tags
- Frontmatter metadata accessible
- Content queryable by type
- No parsing ambiguity

Testing:
- Verified with complex Dataview dashboards
- Tables extract correctly with headers and rows
- All frontmatter fields preserved
- Bundle size: 2.4mb (optimized, no PDF dependencies)

🤖 Generated with Claude Code
https://claude.com/claude-code

Co-Authored-By: Claude <noreply@anthropic.com>
Document the new rendered-text and rendered-json content types,
including usage examples, JSON schema, caching system, and
troubleshooting guide.

- Updated README.md with rendered content overview
- Added comprehensive RENDERED_CONTENT.md guide
- Includes API reference, examples, and use cases
- Documents both text and JSON output formats

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Owner

@coddingtonbear coddingtonbear left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for the patch, @papertray3 — there are some great ideas here! I’m definitely open to supporting a feature that returns rendered HTML when the user supplies an appropriate Accept header (likely text/html, as I noted in a few comments).

I did notice that this PR bundles together a few larger changes that aren’t directly related to that HTML-rendering behavior. To keep things easy to review and to get your work merged more quickly, would you mind splitting this into smaller, focused PRs, each introducing a single change or feature? When you do, a quick update to the docs in /docs would also help people understand how to use the new functionality.

Once you’ve had a chance to break things out, feel free to ping me — I really appreciate the contribution, and I think the rendered-HTML feature in particular will be super useful to a lot of users!


This was inspired by [Vinzent03](https://github.com/Vinzent03)'s [advanced-uri plugin](https://github.com/Vinzent03/obsidian-advanced-uri) with hopes of expanding the automation options beyond the limitations of custom URL schemes.

## Rendered Content Support
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have public docs in the /docs/ folder. Instead of documenting this in the readme, that should probably be in the docs themselves if you could.

@@ -0,0 +1,540 @@
# Rendered Content API
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm betting this is an artifact created when you asked an LLM to help you code this and I bet you didn't intend to include this in the PR.

olrapiNoteJson = "application/vnd.olrapi.note+json",
olrapiNoteHtml = "application/vnd.olrapi.note+html",
olrapiRenderedText = "application/vnd.olrapi.note+rendered-text",
olrapiRenderedJson = "application/vnd.olrapi.note+rendered-json",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgive me, but this PR is already extremely long -- could I trouble you to break this apart into individual PRs for each of these types of content? Only one of these content types appears to be discussed in this PR; so I think sticking to rendered HTML in this one is probably the right choice, and I want to say: I might need some convincing before I'll be up for taking on a patch for the other two rendering options.

Second thing: you don't need to invent a new content type for things like html/text/json -- those already exist:

  • text/html
  • text/plain
  • application/json

);

// Cache statistics
if (this.plugin.renderCacheManager) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a particular performance problem you found that inspired you to want to cache renders? It seems like kind of a lot of complication to add unless there are severe performance problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants